#include "types.h"
#include "bgi.h"

static struct
{
  struct
  {
    size_t size;
    size_t mult;
    size_t marg;
    size_t baseSize;
  } x, y;
  enum FontOrient orientation;
} font = { 0 };

CharImage_t _far *font8x8 = 0;

/*
 * Returns:
 * - closest X character size available in lower 16 bits of result
 * - closest Y character size available in upper 16 bits of result
 */
uint32_t setTextStyle(FontDescr_t fd, int xSize, int ySize)
{
  font.orientation = fd.orientation;
  font.x.baseSize = 8;
  font.y.baseSize = 8;
  font.x.mult = (xSize + font.x.baseSize / 2) / font.x.baseSize;
  font.y.mult = (ySize + font.y.baseSize / 2) / font.y.baseSize;
  font.x.size = font.x.mult * font.x.baseSize;
  font.y.size = font.y.mult * font.y.baseSize;
  font.x.marg = font.x.size - 1;
  font.y.marg = font.y.size - 1;
  return((uint32_t)font.y.size << 16 | font.x.size);
}

/*
 * Returns:
 * - width of the string in pixels in lower 16 bits of result
 * - heigth of the string in pixels in upper 16 bits of result
 */
uint32_t getTextSize(char const _far *string, size_t len)
{
  return(font.x.size * len | (uint32_t)font.y.size << 16);
}

static bool_t getCharBitNormalOrient(CharImage_t _far *charImage, int xCh, int yCh)
{
  return((*charImage)[yCh] & (1 << (font.x.baseSize - xCh - 1)));
}

static bool_t getCharBitVerticalOrient(CharImage_t _far *charImage, int xCh, int yCh)
{
  return((*charImage)[xCh] & (1 << yCh));
}

static bool_t getCharBitDownOrient(CharImage_t _far *charImage, int xCh, int yCh)
{
  return((*charImage)[font.y.baseSize - yCh - 1] & (1 << xCh));
}

static bool_t getCharBitVerticalDownOrient(CharImage_t _far *charImage, int xCh, int yCh)
{
  return((*charImage)[font.x.baseSize - xCh - 1] & (1 << (font.y.baseSize - yCh - 1)));
}

typedef bool_t (*getCharBitFunc_t)(CharImage_t _far *charImage, int xCh, int yCh);

static void DrawLetter(coord_t x, coord_t y, CharImage_t _far *charImage, getCharBitFunc_t getCharBit)
{
  if(x >= vp.x1 && x <= vp.x2 - font.x.marg &&
     y >= vp.y1 && y <= vp.y2 - font.y.marg)
  {
    int i;
    for(i = 0; i < font.y.baseSize; ++i)
    {
      int j;
      for(j = 0; j < font.y.mult; ++j, ++y)
      {
        coord_t currX = x;
        int k;
        for(k = 0; k < font.x.baseSize; ++k)
        {
          int l;
          color_t color = getDrawColor(getCharBit(charImage, k, i));
          for(l = 0; l < font.x.mult; ++l, ++currX)
            PutPixel(currX, y, color);
        }
      }
    }
  }
}

static void TextNormalOrient(uint8_t _far *string, int len)
{
  for(; len != 0; len--, string++, cp.x += font.x.size)
    DrawLetter(cp.x, cp.y, &font8x8[*string], getCharBitNormalOrient);
}

static void TextVerticalOrient(uint8_t _far *string, int len)
{
  for(string += len - 1; len != 0; len--, string--, cp.y += font.y.size)
    DrawLetter(cp.x, cp.y, &font8x8[*string], getCharBitVerticalOrient);
}

static void TextDownOrient(uint8_t _far *string, int len)
{
  for(string += len - 1; len != 0; len--, string--, cp.x += font.x.size)
    DrawLetter(cp.x, cp.y, &font8x8[*string], getCharBitDownOrient);
}

static void TextVerticalDownOrient(uint8_t _far *string, int len)
{
  for(; len != 0; len--, string++, cp.y += font.y.size)
    DrawLetter(cp.x, cp.y, &font8x8[*string], getCharBitVerticalDownOrient);
}

void Text(unsigned char _far *string, int len)
{
  static void (*TextFunc[])(uint8_t _far *, int) =
  {
    TextNormalOrient,
    TextVerticalOrient,
    TextDownOrient,
    TextVerticalDownOrient
  };
  TextFunc[font.orientation](string, len);
}
